package cz.semjob.learning.instances.loader;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import weka.core.Instance;
import weka.core.Instances;
import cz.semjob.document.annotation.RelationWordsPair;
import cz.semjob.document.annotation.metadata.Relation;
import cz.semjob.document.processing.DocumentInstance;
import cz.semjob.document.processing.DocumentProcessingException;
import cz.semjob.document.processing.reader.DocumentInstanceReader;
import cz.semjob.learning.data.TaxonomyLearningException;
import cz.semjob.learning.instances.InstanceMapping;
import cz.semjob.learning.instances.UnlabeledRelationInstance;
import cz.semjob.learning.instances.UnlabeledTextInstance;
import cz.semjob.learning.instances.WekaInstanceHelper;
import cz.semjob.learning.instances.creator.ITextInstanceCreator;
import cz.semjob.learning.relation.IRelationInstanceCreator;

/**
 * The Class InstancesCreator. Creates classification instances of the document
 * instance.
 * 
 * @author ivivivi
 */
@Component
public class InstancesCreator {

	/** The dir. */
	@Autowired
	private DocumentInstanceReader dir;

	/** The weka instance helper. */
	@Autowired
	private WekaInstanceHelper wekaInstanceHelper;

	/**
	 * Creates the instances.
	 * 
	 * @param documentInstances
	 *            the document instances
	 * @param instanceCreator
	 *            the instance creator
	 * @param dataSources
	 *            the data sources
	 * @return the map
	 * @throws InstancesLoadingException
	 *             the instances loading exception
	 */
	public Map<DocumentInstance, List<InstanceMapping>> createInstances(
			List<DocumentInstance> documentInstances,
			ITextInstanceCreator instanceCreator, Object... dataSources)
			throws InstancesLoadingException {
		Map<DocumentInstance, List<InstanceMapping>> documentsInstances = new HashMap<DocumentInstance, List<InstanceMapping>>();

		Instances instances = new Instances("Test Data",
				wekaInstanceHelper.getWekaAttributesInfo(
						instanceCreator.getListOfAttributes(),
						instanceCreator.getName()), 1);

		instances.setClassIndex(instances.numAttributes() - 1);

		for (DocumentInstance documentInstance : documentInstances) {
			List<InstanceMapping> instancesList = createUnlabeledInstances(
					instances, instanceCreator, documentInstance, dataSources);

			documentsInstances.put(documentInstance, instancesList);
		}

		return documentsInstances;
	}

	/**
	 * Creates the instances.
	 * 
	 * @param documentRelationWords
	 *            the document relation words
	 * @param instanceCreator
	 *            the instance creator
	 * @return the map
	 * @throws InstancesLoadingException
	 *             the instances loading exception
	 */
	public Map<DocumentInstance, List<InstanceMapping>> createInstances(
			Map<DocumentInstance, List<RelationWordsPair>> documentRelationWords,
			IRelationInstanceCreator instanceCreator, List<Relation> relations)
			throws InstancesLoadingException {
		Map<DocumentInstance, List<InstanceMapping>> documentsInstances = new HashMap<DocumentInstance, List<InstanceMapping>>();

		Instances instances = new Instances("Test Data",
				wekaInstanceHelper.getWekaAttributesInfo(
						instanceCreator.getListOfAttributes(), relations), 1);

		instances.setClassIndex(instances.numAttributes() - 1);

		for (DocumentInstance documentInstance : documentRelationWords.keySet()) {
			List<InstanceMapping> instancesList = createUnlabeledInstancesForRelation(
					instances, instanceCreator, documentInstance,
					documentRelationWords.get(documentInstance));

			documentsInstances.put(documentInstance, instancesList);
		}

		return documentsInstances;
	}

	/**
	 * Load instances.
	 * 
	 * @param textsPath
	 *            the texts path
	 * @param processedTextsPath
	 *            the processed texts path
	 * @param instanceCreator
	 *            the instance creator
	 * @param dataSources
	 *            the data sources
	 * @return the map
	 * @throws InstancesLoadingException
	 *             the instances loading exception
	 */
	public Map<DocumentInstance, List<InstanceMapping>> loadInstances(
			String textsPath, String processedTextsPath,
			ITextInstanceCreator instanceCreator, Object... dataSources)
			throws InstancesLoadingException {
		Map<DocumentInstance, List<InstanceMapping>> documentsInstances = new HashMap<DocumentInstance, List<InstanceMapping>>();

		Instances instances = new Instances("Test Data",
				wekaInstanceHelper.getWekaAttributesInfo(
						instanceCreator.getListOfAttributes(),
						instanceCreator.getName()), 1);

		instances.setClassIndex(instances.numAttributes() - 1);

		File testDirectory = new File(textsPath);
		for (String filename : testDirectory.list()) {
			File documentFile = new File(filename);
			if (documentFile.isDirectory()
					|| !documentFile.getName().endsWith(".txt")) {
				continue;
			}
			DocumentInstance document;
			try {
				document = dir.readDocumentInstance(
						new File(textsPath + filename),
						new File(processedTextsPath
								+ filename.substring(0, filename.length() - 3)
								+ "conllx"));
			} catch (IOException e) {
				e.printStackTrace();
				throw new InstancesLoadingException(e);
			} catch (DocumentProcessingException e) {
				e.printStackTrace();
				throw new InstancesLoadingException(e);
			}

			List<InstanceMapping> instancesList = createUnlabeledInstances(
					instances, instanceCreator, document, dataSources);

			documentsInstances.put(document, instancesList);
		}
		return documentsInstances;

	}

	/**
	 * Creates the unlabeled instances.
	 * 
	 * @param instances
	 *            the instances
	 * @param instanceCreator
	 *            the instance creator
	 * @param document
	 *            the document
	 * @param dataSources
	 *            the data sources
	 * @return the list
	 * @throws InstancesLoadingException
	 *             the instances loading exception
	 */
	private List<InstanceMapping> createUnlabeledInstances(Instances instances,
			ITextInstanceCreator instanceCreator, DocumentInstance document,
			Object... dataSources) throws InstancesLoadingException {

		List<InstanceMapping> instancesList = new ArrayList<InstanceMapping>();

		List<UnlabeledTextInstance> unlabeledinstances = null;
		try {
			unlabeledinstances = instanceCreator.createUnlabeledTextInstances(
					document, dataSources);
			// if (instances == null) {
			// instances = new Instances("Test Data",
			// wekaInstanceHelper.getWekaAttributesInfo(
			// instanceCreator.getListOfAttributes(),
			// instanceCreator.getName()), 1);
			//
			// instances.setClassIndex(instances.numAttributes() - 1);
			// }
		} catch (TaxonomyLearningException e) {
			e.printStackTrace();
			throw new InstancesLoadingException(e);
		}

		for (UnlabeledTextInstance unlabeledTextInstance : unlabeledinstances) {
			Instance instance = wekaInstanceHelper.createWekaInstance(
					unlabeledTextInstance, instances);
			instances.add(instance);
			instancesList.add(new InstanceMapping(unlabeledTextInstance,
					instance));
		}

		return instancesList;
	}

	/**
	 * Creates the unlabeled instances for relation.
	 * 
	 * @param instances
	 *            the instances
	 * @param instanceCreator
	 *            the instance creator
	 * @param document
	 *            the document
	 * @param candidateRelations
	 *            the candidate relations
	 * @return the list
	 * @throws InstancesLoadingException
	 *             the instances loading exception
	 */
	private List<InstanceMapping> createUnlabeledInstancesForRelation(
			Instances instances, IRelationInstanceCreator instanceCreator,
			DocumentInstance document,
			List<RelationWordsPair> candidateRelations)
			throws InstancesLoadingException {

		List<InstanceMapping> instancesList = new ArrayList<InstanceMapping>();

		List<UnlabeledRelationInstance> unlabeledinstances = null;
		try {
			unlabeledinstances = instanceCreator
					.createRelationUnlabeledInstances(document,
							candidateRelations);
		} catch (TaxonomyLearningException e) {
			e.printStackTrace();
			throw new InstancesLoadingException(e);
		}

		for (UnlabeledTextInstance unlabeledTextInstance : unlabeledinstances) {
			Instance instance = wekaInstanceHelper.createWekaInstance(
					unlabeledTextInstance, instances);
			instances.add(instance);
			instancesList.add(new InstanceMapping(unlabeledTextInstance,
					instance));
		}

		return instancesList;
	}

	/**
	 * Sets the dir.
	 * 
	 * @param dir
	 *            the new dir
	 */
	public void setDir(DocumentInstanceReader dir) {
		this.dir = dir;
	}

	/**
	 * Sets the weka instance helper.
	 * 
	 * @param wekaInstanceHelper
	 *            the new weka instance helper
	 */
	public void setWekaInstanceHelper(WekaInstanceHelper wekaInstanceHelper) {
		this.wekaInstanceHelper = wekaInstanceHelper;
	}

}
